iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 14
2
Modern Web

學習 vue 30天系列 第 14

Vue 14 挑戰 TO DO LIST

  • 分享至 

  • xImage
  •  

到昨天的學習下一章節就是挑戰做 todolist,我想挑戰不看老師的教學試著做出 todolist。這邊把主要的新增、刪除以及顯示已完成做項目出來就好,好那要開始囉!!!

預先載入的資源

在開始以前先把以下東西載入到 head 裡:

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel='stylesheet' id='fontawesome-css' href='https://use.fontawesome.com/releases/v5.0.1/css/all.css?ver=4.9.1' type='text/css' media='all' />
    <link href="https://fonts.googleapis.com/css?family=Varela+Round&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="css/reset.css">
    <link rel="stylesheet" href="css/style.css">
    <script src="https://unpkg.com/vue"></script>
    <title>ToDoList</title>
</head>

HTML、CSS 畫面

HTML:

畫面上最主要的東西有三個:1. Input textBox(輸入的部分) 2. List 表單(顯示及check) 3. 切換畫面。

<body>
    <div id="app">
        <div class="wrap">
            <h1>Todos</h1>
            <div class="list">
                <ul>
                    <li>
                        <hr>
                    </li>
                </ul>
            </div>
            <div class="inputBox">
                <input type="text" class="textBox">
            </div>
            <div class="finishOrNot">
                <span></span>
                <span></span>
            </div>
        </div>
    </div>
</body>

CSS:

這邊比較需要說明的是,<li> 加入時是反向的加入,所以這邊就讓 <ul> 轉180度以及讓 <li> 轉-180度就能達到有下往上<li>的加入。

* {
    box-sizing: border-box;
}

html,
body {
    width: 100%;
    height: 100%;
}

body {
    display: block;
    color: #2E2E2E;
    font-size: 18px;
    font-family: 'Varela Round', sans-serif;
    line-height: 1.5;
}

#app {
    display: flex;
    align-items: center;
    flex-direction: column;
    justify-content: center;
    height: 100%;
    height: 100%;
    background: #bd83ce;
    background: linear-gradient(45deg, #bd83ce, #ff9068);
}

.wrap {
    display: flex;
    align-items: center;
    flex-direction: column;
    justify-content: space-between;
    padding: 3rem;
    min-width: 380px;
    min-height: 480px;
    max-width: 50%;
    max-height: 100%;
    border-radius: 5px;
    background: #fafafa;
    box-shadow: 0 0 5px rgba(25, 25, 25, 0.25);
}

.wrap>h1 {
    font-size: 2em;
    font-family: 'Varela Round', sans-serif;
}

.list {
    display: flex;
    overflow: scroll;
    flex-direction: column-reverse;
    width: 200px;
    height: 150px;
}

.list>ul {
    transform: rotate(180deg);
}

.list>ul>li {
    width: 100%;
    transform: rotate(-180deg);
}

hr {
    margin: 10px 0;
    border: 1px solid rgb(202, 201, 201);
}

.inputBox {
    margin-top: 15px;
    width: 100%;
}

.textBox {
    margin: 0;
    padding: .5rem 1rem;
    width: 100%;
    outline: 0;
    border: 2px solid #e8ebed;
    border-radius: 5px;
    background: #e8ebed;
    transition: background .25s ease-out;
}

.doneOrTodo {
    display: flex;
    justify-content: space-between;
    width: 100%;
}

.doneOrTodo span {
    margin: 0 50px 0 50px;
}

.doneOrTodo>span:first-child::before {
    display: inline-block;
    color: #3498db;
    content: "\f0ca";
    font-weight: 900;
    font-size: 25px;
    font-family: 'Font Awesome 5 Free';
}

.doneOrTodo>span:last-child::before {
    display: inline-block;
    color: #2ecc71;
    content: "\f00c";
    font-weight: 900;
    font-size: 25px;
    font-family: 'Font Awesome 5 Free';
}

.complete {
    text-decoration: line-through;
}

.visible {
    display: none;
}

Vue

開始做 Vue application 時先想好我們會需要用到哪些的資料,以及其儲存的資料結構:

data: {
    newTodo: "", // 用 v-model 來獲取 textBox 當下裡面的值
    todos: [], // 把 textBox 裡面的值存到此陣列中,在藉由此陣列來把輸入的項目渲染到畫面上
    done: [], // 儲存已完成的項目
    notFinish: false, // 切換畫面用
    finish: true, // 切換畫面用

}

建立完資料結構後就是要做 methods 的處理

Input textBox(輸入的部分)

第一步
我們先把 textBox 裡面的值動態得存入 newTodo 裡,使用 v-model 來達成。

第二步
@keyup.enter 這個 event handler 來觸發 addTodo,這個 addTodo 是把 newTodo 這個變數加入到 todos 這個陣列裡,加入到陣列後把 newTodo 變為空,藉此來把 textBox 也變成空的(沒輸入任何東西的狀態)。

//HTML
<div class="inputBox">
    <input type="text" class="textBox" @keyup.enter="addTodo" v-model="todo">
</div>

//VUE
addTodo(event) {
    this.todos.push(this.newTodo);
    this.newTodo = "";
}
//

List 表單(顯示及check)

第一步
先把 todos 陣列裡面的值全部打印到畫面上,這邊用 v-for 來逐一抓出資料並顯示。

第二步
當 check 的時候用 @click event handler 來觸發 getData function,這個 function 會把當前點擊的值給存到 done 這個陣列裡。

//HTML
<div class="list">
    <ul @click="getData">
        <li v-for="(item,index) in todos">
            {{item}}
            <hr>
        </li>
    </ul>
</div>

//VUE
getData(event) {

    // 取得點擊的元素,也可以直接這樣用 event.target 就不用多宣告變數
    const { target } = event;
    
    // 判斷是不是點擊 <li> tag
    if (target.tagName === "LI") { 
    
        // target.textContent 裡面會包含空格所以要用 trim() 來移除
        this.done.push(target.textContent.trim()); 
        
        // 刪除剛點擊的元素
        this.todos.splice(this.todos.indexOf(target.textContent.trim()), 1); 
    };
}

切換畫面

我用比較簡單的方式來作切換畫面,這邊就再多做一個 ul 是顯示已完成的項目(done 陣列),在用 display:none 來做畫面的切換。

第一步
先用 v-bind 來綁定 visibledisplay:none) 這個 class ,在用兩個 flag(notFinish、finish),來做切換的依據。

第二步
@click event handler 來觸發 changeTodochangeDone,這兩個 function 是來判斷要不要讓 visible class 加入到元素上。

<div class="list">
    <ul @click="getData" :class="{'visible':notFinish}">
        <li v-for="(item,index) in todos">
            {{item}}
            <hr>
        </li>
    </ul>
    <ul :class="{'visible':finish}">
        <li class="complete" v-for="(item,index) in done">
            {{item}}
            <hr>
        </li>
    </ul>
</div>

<div class="finishOrNot">
    <span @click="changeTodo"></span>
    <span @click="changeDone"></span>
</div>

//VUE
changeTodo(event) {
    this.notFinish = false;
    this.finish = true;
},
changeDone(event) {
    this.notFinish = true;
    this.finish = false;
}

全部完成的 CODE

<div id="app">
    <div class="wrap">
        <h1>Todos</h1>
        <div class="list">
            <ul @click="getData" :class="{'visible':notFinish}">
                <li v-for="(item,index) in todos">
                    {{item}}
                    <hr>
                </li>
            </ul>
            <ul :class="{'visible':finish}">
                <li class="complete" v-for="(item,index) in done">
                    {{item}}
                    <hr>
                </li>
            </ul>
        </div>
        <div class="inputBox">
            <input type="text" class="textBox" @keyup.enter="addTodo" v-model="newTodo">
        </div>
        <div class="finishOrNot">
            <span @click="changeTodo"></span>
            <span @click="changeDone"></span>
        </div>
    </div>
</div>

<script>
    let app = new Vue({
        el: '#app',
        data: {
            newTodo: "",
            todos: [],
            done: [],
            notFinish: false,
            finish: true,

        },
        methods: {
            addTodo(event) {
                this.todos.push(this.newTodo);
                this.newTodo = "";
            },
            getData(event) {
                const {
                    target
                } = event;
                if (target.tagName === "LI") {
                    this.done.push(target.textContent.trim());
                    this.todos.splice(this.todos.indexOf(target.textContent.trim()), 1);
                };
            },
            changeTodo(event) {
                this.notFinish = false;
                this.finish = true;
            },
            changeDone(event) {
                this.notFinish = true;
                this.finish = false;
            }
        }
    });
</script>

後記

我有想過,如果能把 v-for 裡面的陣列動態的切換的話,就不用兩個 ul tag 了,不知道這樣行不行得通。
明天來看老師的影片,看看有沒有什麼好用的方法來製作。


上一篇
Vue 13 元件基礎概念
下一篇
Vue 15 進階模板語法介紹 (1) - 資料細節及動態切換 class name 及 style 多種方法
系列文
學習 vue 30天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言